package net.qiqbframework.messaging.annotation;

import java.lang.reflect.Executable;
import java.util.Comparator;
import java.util.function.Function;


public final class HandlerComparator {

    private static final Comparator<MessageExecutableHandler> INSTANCE =
            Comparator.comparingInt(  MessageExecutableHandler::priority)
                    .reversed()
                    .thenComparing(m -> !isResultHandler(m))
                    .thenComparing((memberOne, memberTwo) -> {
                        Comparator<MessageExecutableHandler> handlerComparator =
                                Comparator.comparing(
                                                (Function<MessageExecutableHandler, Class<?>>) MessageExecutableHandler::payloadType,
                                                HandlerComparator::compareHierarchy
                                        )
                                        .thenComparing(Comparator.comparingInt(HandlerComparator::parameterCount)
                                                .reversed())
                                        .thenComparing(HandlerComparator::executableSignature);

                        return isResultHandler(memberOne) || isResultHandler(memberTwo)
                                ? handlerComparator.reversed().compare(memberOne, memberTwo)
                                : handlerComparator.compare(memberOne, memberTwo);
                    });

    private static boolean isResultHandler(MessageExecutableHandler m) {
        return m.attribute("ResultHandler.resultType").isPresent();
    }

    private HandlerComparator() {

    }

    /**
     * Returns the singleton comparator managed by the HandlerComparator class.
     *
     * @return the singleton comparator
     */
    public static Comparator<MessageExecutableHandler> instance() {
        return INSTANCE;
    }

    private static int compareHierarchy(Class<?> o1, Class<?> o2) {
        if (o1.isInterface() && !o2.isInterface()) {
            return 1;
        } else if (!o1.isInterface() && o2.isInterface()) {
            return -1;
        }
        return Integer.compare(depthOf(o2), depthOf(o1));
    }

    private static int depthOf(Class<?> o1) {
        int depth = 0;
        Class<?> type = o1;
        if (o1.isInterface()) {
            while (type.getInterfaces().length > 0) {
                depth++;
                type = type.getInterfaces()[0];
            }
        } else {
            while (type != null) {
                depth++;
                type = type.getSuperclass();
            }
        }
        if (o1.isAnnotation()) {
            depth += 1000;
        }
        return depth;
    }

    private static Integer parameterCount(MessageExecutableHandler handler) {
        return handler.unwrap(Executable.class)
                .map(Executable::getParameterCount)
                .orElse(1);
    }

    private static String executableSignature(MessageExecutableHandler handler) {
        return handler.unwrap(Executable.class)
                .map(Executable::toGenericString)
                .orElse(handler.toString());
    }
}
